package com.yznote.cloudesk.aop;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.yznote.cloudesk.common.Anno.CollectLogs;
import com.yznote.cloudesk.common.pojo.Result;
import com.yznote.cloudesk.common.pojo.User;
import com.yznote.cloudesk.common.pojo.YLogs;
import com.yznote.cloudesk.common.tokenShare.YTokenShare;
import com.yznote.cloudesk.common.utils.IPUtils;
import com.yznote.cloudesk.common.utils.IdUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.rocketmq.client.producer.SendCallback;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.reflections.serializers.Serializer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

/**
 * @author : xiaozhoutongxue
 * @date : 2022/11/8 16:00
 */

@Aspect
@Slf4j
@Component
public class CollectLogsAOP {

    @Autowired
    YTokenShare yTokenShare;
    @Autowired
    RocketMQTemplate rocketMQTemplate;


    //保证时间为线程私有
    private ThreadLocal<LocalDateTime> threadLocal = new ThreadLocal();

    @Pointcut("@annotation(com.yznote.cloudesk.common.Anno.CollectLogs) || @within(com.yznote.cloudesk.common.Anno.CollectLogs)")
    public void execAop() {

    }
    @Before(value = "execAop()")
    public void before() {
        threadLocal.set(LocalDateTime.now());
    }

    @AfterThrowing(pointcut = "execAop()", throwing = "e")
    public void execute(JoinPoint jp, Exception e) {
        Annotation[] declaredAnnotations = ((MethodSignature) jp.getSignature()).getMethod().getDeclaredAnnotations();
        if(declaredAnnotations==null||declaredAnnotations.length==0){
            // 为类中非接口方法 停止日志记录
            return;
        }
        YLogs ylogs = handle(jp);
        ylogs.setStatus(1);
        Map map = new HashMap();
        map.put("message",e.getMessage());
        map.put("class",e.getClass());
        map.put("cause",e.getCause());
        ylogs.setRequestResult(JSON.toJSONString(map));
        rocketMQTemplate.asyncSend("logsToMysql", JSON.toJSONString(ylogs), new SendCallback() {
            @Override
            public void onSuccess(SendResult sendResult) {
            }

            @Override
            public void onException(Throwable throwable) {
                log.error("rocketmq error!" + ylogs.getLogsId());
            }
        }, 10000L);
    }

    @AfterReturning(pointcut = "execAop()", returning = "ret")
    public void after(JoinPoint jp, Object ret) throws Exception {
        Annotation[] declaredAnnotations = ((MethodSignature) jp.getSignature()).getMethod().getDeclaredAnnotations();
        if(declaredAnnotations==null||declaredAnnotations.length==0){
            // 为类中非接口方法 停止日志记录
            return;
        }
        YLogs ylogs = handle(jp);
        String r = JSON.toJSONString(ret);
        // 尝试用Result强转
        if (ylogs.getRequestResult()==null) {
            // 使用了needResponse 只记录code和msg
            if(ret instanceof Result){
                Result r1 = (Result) ((Result)ret).clone();
                r1.setData("不记录数据");
                ylogs.setRequestResult(JSON.toJSONString(r1));
            }
            else ylogs.setRequestResult("{\"error\":\"返回类型不支持\"}");
        } else if (r != null && r.length() <= 3000) {
            if(ret instanceof Result) ylogs.setRequestResult(r);
            else ylogs.setRequestResult("{\"error\":\"返回类型不支持\"}");
        } else if (r == null) {
            ylogs.setRequestResult("{\"info\":\"返回结果为空\"}");
        } else {
            // 超出长度限制
            if(ret instanceof Result){
                    Result r1 = (Result) ((Result)ret).clone();
                    r1.setData("数据超过长度限制");
                    ylogs.setRequestResult(JSON.toJSONString(r1));
            }
            else ylogs.setRequestResult("{\"error\":\"返回类型不支持\"}");
        }
        rocketMQTemplate.asyncSend("logsToMysql", JSON.toJSONString(ylogs), new SendCallback() {
            @Override
            public void onSuccess(SendResult sendResult) {
            }

            @Override
            public void onException(Throwable throwable) {
                log.error("rocketmq error!" + ylogs.getLogsId());
            }
        }, 10000L);
    }

    public YLogs handle(JoinPoint jp) {
        YLogs yLogs = new YLogs();
        if (yTokenShare.get() != null) {
            User user = yTokenShare.get();
            yLogs.setUserId(user.getUserId());
        }
        yLogs.setLogsId(IdUtils.nextId());
        //请求发起时间
        yLogs.setRequestTime(threadLocal.get());
        //请求响应时间
        Duration between = Duration.between(threadLocal.get(), LocalDateTime.now());
        Long cost = between.toMillis();
        yLogs.setCostTime(cost);
        //获得织入方法
        Method method = ((MethodSignature) jp.getSignature()).getMethod();
        // 获得日志模块
        CollectLogs classAnnotation = jp.getTarget().getClass().getAnnotation(CollectLogs.class);
        //获取注解实例
        CollectLogs methodAnnotation = method.getDeclaredAnnotation(CollectLogs.class);
        Boolean needParam = null;
        String moduleName = null;
        Boolean needResult = null;
        if(classAnnotation==null&&methodAnnotation==null){
            log.error("发生系统错误");
            return null;
        }else if(classAnnotation!=null&&methodAnnotation==null){
            // 以类上面注解为主
            needParam = classAnnotation.needParam();
            needResult = classAnnotation.needResponse();
            moduleName = classAnnotation.moduleName();
        }else{
            // 以方法上面注解为主
            needParam = methodAnnotation.needParam();
            needResult = methodAnnotation.needResponse();
            moduleName = methodAnnotation.moduleName();
        }
        yLogs.setModuleName(moduleName);
        //方法名
        yLogs.setMethodName(method.getName());
        //如果需要参数
        if (needParam) {
            //获得请求实参
            Object[] args = jp.getArgs();
            // 获得织入方法的形参
            Parameter[] parameters = method.getParameters();
            Map<String, Object> map = new HashMap<>();
            for (int i = 0; i < args.length; i++) {
                map.put(parameters[i].getName(), args[i]);
            }
            String s = StringEscapeUtils.unescapeJava(JSON.toJSONString(map, SerializerFeature.IgnoreErrorGetter));
            if(s.length()>1000){
                yLogs.setRequestParam("{\"msg\":\"超过长度限制\"}");
            }else {
                yLogs.setRequestParam(s);
            }
        }else {
            yLogs.setRequestParam("{\"msg\":\"不记录参数\"}");
        }
        //获得方法请求方式
        Annotation[] declaredAnnotations = method.getDeclaredAnnotations();
        for (int i = 0; i < declaredAnnotations.length; i++) {
            String simpleName = declaredAnnotations[i].annotationType().getSimpleName();
            if ("PostMapping".equals(simpleName)) {
                yLogs.setRequestType("PostMapping");
                break;
            } else if ("GetMapping".equals(simpleName)) {
                yLogs.setRequestType("GetMapping");
                break;
            }
        }
        yLogs.setStatus(0);
        yLogs.setRequestIp(IPUtils.getRequestIp());
        if(needResult) yLogs.setRequestResult("");
        return yLogs;
    }


}
